package LimitedTimeGame.Day_0215;

/**
 * @author zxc
 * @date 2023/02/15 16:04
 **/

import java.util.HashMap;
import java.util.List;
import java.util.Stack;

/**
 * 题目 ：函数独占时间
 * 题目详述 ：
 * 有一个 单线程 CPU 正在运行一个含有 n 道函数的程序。每道函数都有一个位于 0 和 n-1 之间的唯一标识符。
 * 函数调用 存储在一个 调用栈 上 ：当一个函数调用开始时，它的标识符将会推入栈中。
 * 而当一个函数调用结束时，它的标识符将会从栈中弹出。标识符位于栈顶的函数是 当前正在执行的函数 。
 * 每当一个函数开始或者结束时，将会记录一条日志，包括函数标识符、是开始还是结束、以及相应的时间戳。
 * 给你一个由日志组成的列表 logs ，
 * 其中 logs[i] 表示第 i 条日志消息，该消息是一个按 "{function_id}:{"start" | "end"}:{timestamp}" 进行格式化的字符串。
 * 例如，"0:start:3" 意味着标识符为 0 的函数调用在时间戳 3 的 起始开始执行 ；
 * 而 "1:end:2" 意味着标识符为 1 的函数调用在时间戳 2 的 末尾结束执行。注意，函数可以 调用多次，可能存在递归调用 。
 * 函数的 独占时间 定义是在这个函数在程序所有函数调用中执行时间的总和，调用其他函数花费的时间不算该函数的独占时间。
 * 例如，如果一个函数被调用两次，一次调用执行 2 单位时间，另一次调用执行 1 单位时间，那么该函数的 独占时间 为 2 + 1 = 3 。
 * 以数组形式返回每个函数的 独占时间 ，其中第 i 个下标对应的值表示标识符 i 的函数的独占时间。
 *
 * 提示：
 * 1 <= n <= 100
 * 1 <= logs.length <= 500
 * 0 <= function_id < n
 * 0 <= timestamp <= 109
 * 两个开始事件不会在同一时间戳发生
 * 两个结束事件不会在同一时间戳发生
 * 每道函数都有一个对应"start" 日志的 "end" 日志
 *
 */
public class ExclusiveTime {
    /**
     * 思路 ：
     * 即，解析logs集合，来获取每条log日志的详细信息;
     * （需要注意的是，log日志是按照时间顺序来进行存储的;）
     * ===>
     * 具体实现 ：
     * （1）创建结果数组result，用来存储每个线程所独占的时间总数;
     * （2）创建栈stack，用来存储已经开始，但是还未结束的线程名称;
     * （3）遍历logs集合，获取集合中所有log信息;
     *  ===》 即，使用":"进行分割，来获取 当前执行线程 + 执行操作（start/end） + 当前时间
     *  注意 ：由于当前时间有start和end之分，所以 n-start == n; n-end == n+1;
     * （4）同时，需要定义一个变量去保存上一个log日志中的时间，以便计算上一条log到当前log之间的时间差;
     * （5）若是当前log中内容是"start"，则选择将当前线程压入栈中 && 同时将previousTime 更新为 currentTime;
     *  若是当前log中内容是"end"，则选择将栈顶线程弹出 && 同时将previousTime 更新为 currentTime + 1;
     *  ===》
     *  注意 ：即，在线程A中，调用线程B，不可能出现线程A先结束而线程B，后结束的情况，所以在"end"时，必然是栈顶正在运行的线程;
     *  即，线程之间的调用是层层嵌套的，当前运行的线程是依赖于调用其的线程的;
     *
     * @param n
     * @param logs
     * @return
     */
    public int[] exclusiveTime(int n, List<String> logs) {
        Stack<Integer> stack = new Stack<>();
        int[] result = new int[n];
        int previousTime = 0;

        for (String log : logs) {
            String[] dp = log.split(":");
            int currentTime = Integer.valueOf(dp[2]);
            if(dp[1].equals("start")){
                if(!stack.isEmpty()) {
                    result[stack.peek()] += currentTime - previousTime;
                }
                stack.push(Integer.valueOf(dp[0]));
                previousTime = currentTime;
            }
            else {
                if(!stack.isEmpty()){
                    result[stack.peek()] += currentTime - previousTime + 1;
                }
                stack.pop();
                previousTime = currentTime + 1;
            }
        }
        return result;
    }
}
